package com.webserver.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
/*
 * 静态导入允许我们导入某个类的静态资源，这样在当前类中使用时就无需再用该类的类名.引用了
 */
import static com.webserver.http.HttpContext.CR;
import static com.webserver.http.HttpContext.LF;

/**
 * HttpRequest-请求对象
 * 该类的每一个实例用于表示浏览器发送过来的一个Http请求
 * 每个请求包含三部分:请求行，消息头，消息正文
 * @author pc
 *
 */
public class HttpRequest {
	//请求行相关信息
	private String method;//请求方式
	private String uri;//抽象路径
	private String protocol;//协议版本
	
	private String requestURI;//抽象路径中的请求部分(?左侧内容)
	private String queryString;//抽象路径中的参数部分(?右侧内容)
	private Map<String,String> parameters = new HashMap<>();//保存每一个参数
	
	//消息头相关信息
	private Map<String,String> headers = new HashMap<>();
	
	
	//消息正文相关信息
	
	
	
	
	private Socket socket;
	private InputStream in;//通过socket获取的输入流，用于读取客户端发送过来的内容
	/**
	 * 实例化HttpRequest的过程就是解析过程，通过读取客户端发送过来的请求内容来实例化
	 * 请求对象。实例化后该对象表示当前客户端发送过来的请求。
	 * @throws EmptyRequestException 
	 */
	public HttpRequest(Socket socket) throws EmptyRequestException {
		System.out.println("HttpRequest:开始解析请求...");
		try {
			this.socket = socket;
			this.in = socket.getInputStream();
			/*
			 * 1:解析请求行
			 * 2:解析消息头
			 * 3:解析消息正文
			 */
			parseRequestLine();
			parseHeaders();
			parseContent();
		} catch(EmptyRequestException e) {
			throw e;
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("HttpRequest:解析请求完毕!");
	}
	/**
	 * 解析请求行
	 * @throws EmptyRequestException 
	 */
	private void parseRequestLine() throws EmptyRequestException {
		System.out.println("HttpRequest:开始解析请求行...");
		try {
			String line = readLine();
			//判断是否为空请求
			if(line.isEmpty()) {
				//对外抛出空请求异常
				throw new EmptyRequestException();
			}
			
			System.out.println("请求行:"+line);
			/*
			 * 将请求行按照空格拆分为三部分，并分别赋值给对应的属性：
			 * method,uri,protocol
			 */
			String[] data = line.split("\\s");
			method = data[0];
			uri = data[1];
			protocol = data[2];
			
			System.out.println("method:"+method);
			System.out.println("uri:"+uri);
			System.out.println("protocol:"+protocol);
			
			parseUri();//进一步解析uri
			
		//单独捕获空请求异常，目的是继续抛出给构造方法	
		} catch(EmptyRequestException e) {
			throw e;
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("HttpRequest:解析请求行完毕!");
	}
	/**
	 * 进一步解析URI
	 */
	private void parseUri() {
		System.out.println("HttpRequest:进一步解析uri...");
		/*
		 * uri有两种情况:带参数与不带参数
		 * 不带参数的如:/myweb/index.html
		 * 不带参数的这种情况我们直接将uri赋值给requestURI即可。
		 * 
		 * 带参数的如:/myweb/reg?username=xxx&password=xxx&....
		 * 这种情况我们需要做以下操作:
		 * 1:先将uri按照?拆分为请求部分和参数部分，并将请求部分赋值给requestURI
		 *   将参数部分赋值给queryString
		 * 2:再进一步解析参数部分，将queryString按照&拆分出每一个参数，然后每个
		 *   参数再按照=拆分为参数名与参数值，并将参数名作为key,参数值作为value
		 *   保存到parameters这个Map中。  
		 *   要考虑当输入框不输入信息时提交的情况，避免下标越界
		 */
		//判断uri是否含有?
		if(uri.indexOf("?")!=-1) {
			String[] data = uri.split("\\?");
			requestURI = data[0];
			if(data.length>1) {
				queryString = data[1];
				//先对queryString转码，将%XX内容还原对应文字
				try {
					System.out.println("转码前queryString:"+queryString);
					queryString = URLDecoder.decode(queryString, "UTF-8");
					System.out.println("转码后queryString:"+queryString);
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}				
				//拆分每一个参数
				data = queryString.split("&");
				for(String para : data) {//para:name=value
					String[] arr = para.split("=");
					if(arr.length>1) {
						parameters.put(arr[0], arr[1]);
					}else {
						parameters.put(arr[0],null);
					}
				}
			}		
		}else {
			//没有?
			requestURI = uri;
		}
		
		System.out.println("requestURI:"+requestURI);
		System.out.println("queryString:"+queryString);
		System.out.println("parameters:"+parameters);
		
		System.out.println("HttpRequest:进一步解析uri完毕!");
	}
	
	/**
	 * 解析消息头
	 */
	private void parseHeaders() {
		System.out.println("HttpRequest:开始解析消息头...");
		try {
			/*
			 * 循环调用readLine方法读取每一个消息头，如果某次readLine方法返回
			 * 的是一个空字符串，那么说明单独读取到了CRLF，此时应当停止解析消息
			 * 头的工作。
			 * 读取到的每一个消息头要按照冒号空格拆分为两部分，分别是消息头的
			 * 名字和消息头的值，分别作为key和value保存到headers这个属性中
			 */
			String line;
			while(!(line = readLine()).equals("")) {
				System.out.println("消息头:"+line);
				String[] data = line.split(":\\s");
				headers.put(data[0],data[1]);
			}
			
			
			System.out.println("headers:"+headers);
		} catch (Exception e) {
			e.printStackTrace();
		}System.out.println("HttpRequest:解析消息头完毕!");
	}
	/**
	 * 解析消息正文
	 */
	private void parseContent() {
		System.out.println("HttpRequest:开始解析消息正文...");
		
		System.out.println("HttpRequest:解析消息正文完毕!");
	}
	
	/**
	 * 读取一行字符串
	 * @return
	 * @throws IOException 
	 */
	private String readLine() throws IOException {
		int d = 0;
		StringBuilder builder = new StringBuilder();
		while((d = in.read())!=-1) {
			char c = (char)d;//本次读取到的字符
			//是否连续读取到了CRLF，(上次读取的字符是CR，本次读取的是LF)
			if(builder.length()!=0 &&
			   builder.charAt(builder.length()-1)==CR &&
			   c==LF) {
				break;
			}
			builder.append(c);
		}
		return builder.toString().trim();
	}
	public String getMethod() {
		return method;
	}
	public String getUri() {
		return uri;
	}
	public String getProtocol() {
		return protocol;
	}
	public String getHeader(String name) {
		return headers.get(name);
	}
	public String getRequestURI() {
		return requestURI;
	}
	public String getQueryString() {
		return queryString;
	}
	public String getParameter(String name) {
		return parameters.get(name);
	}
	
	
}












